"""
Unified Notification Service
============================

Provides a consolidated, stateless service for all notification operations,
including email, SMS, and push notifications. It handles the business logic
for creating notification records and dispatching them through the appropriate channel.
"""
import logging
from typing import Dict, Any, Optional
import aiosmtplib
from email.mime.text import MIMEText
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from datetime import datetime

from backend.shared import DatabaseFactory as UnifiedDatabaseManager
from backend.shared import RedisCache as RedisManager
from ..core.exceptions import NotificationError # Assuming a custom exception class

logger = logging.getLogger(__name__)

# ----------------- Channel-Specific Logic -----------------
# In a larger service, these would be in their own files (e.g., `_email_handler.py`)

class _EmailHandler:
    """Internal handler for sending emails via SMTP."""
    def __init__(self, config: Dict[str, Any]):
        if not config or not config.get("enabled", False):
            raise ValueError("Email handler is not enabled or configured.")
        
        self.smtp_host = config.get('smtp_host')
        self.smtp_port = config.get('smtp_port', 587)
        self.username = config.get('username')
        self.password = config.get('password')
        
        if not all([self.smtp_host, self.username, self.password]):
            raise ValueError("SMTP host, username, and password must be configured for email.")

    async def send(self, recipient: str, subject: str, content: str, sender: Optional[str] = None):
        """Sends a single email."""
        message = MIMEText(content, 'plain', 'utf-8')
        message['From'] = sender or self.username
        message['To'] = recipient
        message['Subject'] = subject

        try:
            await aiosmtplib.send(
                message,
                hostname=self.smtp_host,
                port=self.smtp_port,
                start_tls=True,
                username=self.username,
                password=self.password
            )
            logger.info(f"Email sent successfully to {recipient}.")
        except Exception as e:
            logger.error(f"Failed to send email to {recipient}: {e}", exc_info=True)
            raise NotificationError(f"SMTP error: {e}")

# ----------------- Main Service Class -----------------

class NotificationService:
    """The unified, stateless notification service."""

    def __init__(self, db_manager: UnifiedDatabaseManager, redis_manager: RedisManager, config: dict):
        self.db_manager = db_manager
        self.redis_manager = redis_manager
        self.config = config
        self._email_handler = None

        # Initialize handlers based on configuration
        if self.config.get("email", {}).get("enabled"):
            try:
                self._email_handler = _EmailHandler(self.config["email"])
                logger.info("Email notification handler initialized.")
            except ValueError as e:
                logger.warning(f"Could not initialize email handler: {e}")

        # ... Initialize SMS and Push handlers here in the same way ...

        logger.info("Unified NotificationService initialized.")

    async def send_email(
        self,
        db_session: AsyncSession,
        user_id: str,
        recipient: str,
        subject: str,
        content: str
    ) -> str:
        """
        Creates an email notification record and dispatches it.
        
        Returns:
            The ID of the created notification record.
        """
        if not self._email_handler:
            raise NotificationError("Email service is not configured or enabled.")

        notification_id = None
        status = "PENDING"
        error_message = None

        try:
            # 1. Attempt to send the email first
            await self._email_handler.send(recipient, subject, content)
            status = "SENT"
        except NotificationError as e:
            status = "FAILED"
            error_message = str(e)
            logger.error(f"Dispatch failed for email to {recipient}: {error_message}")
        
        # 2. Log the attempt to the database regardless of outcome
        try:
            stmt = text("""
                INSERT INTO notifications (user_id, type, recipient, subject, content, status, error_message, dispatched_at)
                VALUES (:user_id, 'EMAIL', :recipient, :subject, :content, :status, :error_message, :dispatched_at)
                RETURNING id;
            """)
            result = await db_session.execute(stmt, {
                "user_id": user_id,
                "recipient": recipient,
                "subject": subject,
                "content": content,
                "status": status,
                "error_message": error_message,
                "dispatched_at": datetime.utcnow() if status == "SENT" else None
            })
            notification_id = result.scalar_one()
            await db_session.commit()
            logger.info(f"Logged notification {notification_id} with status {status} for user {user_id}.")
        except Exception as e:
            await db_session.rollback()
            logger.critical(f"Failed to log notification to DB for user {user_id} after dispatch attempt: {e}", exc_info=True)
            # If DB logging fails, we lose the record of the attempt, which is a critical issue.
            # The original error from sending is lost if this happens, so we raise a new, more severe error.
            raise NotificationError("Critical error: Failed to log notification record to database.")
            
        return str(notification_id)

    # ... Other methods like `send_sms` and `send_push` would follow a similar pattern ...
